this的指向
this绑定规则一共有5种:
- 默认绑定(严格/非严格模式)
- 隐式绑定
- 显式绑定
- new绑定
- 箭头函数绑定
普通方法的this指向(默认绑定 严格/非严格模式)
| 1 | // 非严格模式 | 
| 1 | // 非严格模式 | 
这个例子中let没有给顶层对象中(浏览器是window)添加属性,window.name2和window.doSth都是undefined。
严格模式中,普通函数中的this则表现不同,表现为undefined。
| 1 | // 严格模式 | 
对象方法的this指向(隐式绑定)
this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象
- 如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window
- 如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象
- 如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象
| 1 | var person = { | 
构造函数的this指向(new绑定)
| 1 | function Foo(name,age){ | 
构造函数的 this 指向实例的,因为 new 的步骤
- 创建一个新的空对象
- 新对象的 __proto__ 属性指向构造函数的原型对象
- 把构造函数的this设置为新创建的对象
- 执行构造函数的代码
- 构造函数没有return语句,则将该新创建的对象返回
new 的模拟实现
| 1 | function myNew(Func, ...args) { | 
优化版
__proto__在IE浏览器中不支持,使用 Object.create(),作用如下
| 1 | let x = { name: "lsh" }; | 
| 1 | function myNew(Func, ...args) { | 
Object.create() 的模拟
| 1 | function newCreate(prototype, propertiesObject) { | 
原型链中的this指向
| 1 | function Student(name){ | 
会发现这个似曾相识。这就是对象上的方法调用模式。自然是指向生成的新对象。 如果该对象继承自其它对象。同样会通过原型链查找。 上面代码使用 ES6中class写法则是:
| 1 | class Student{ | 
当this碰到return
如果返回值是一个对象({},[],RegExp, Date, Function),那么this指向的就是那个返回的对象,如果返回值不是一个对象(基本类型)那么this还是指向函数的实例
| 1 | function Foo(name,age){ | 
箭头函数的this指向(箭头函数绑定)
- 箭头函数本身没有 - this、- super、- arguments和- new.target绑定
- 不能使用 - new来调用
- 没有原型对象 
- 不可以改变 - this的绑定
- 形参名称不能重复 
箭头函数中没有this绑定,必须通过查找作用域链来决定其值。 如果箭头函数被非箭头函数包含,则this绑定的是最近一层非箭头函数的this,否则this的值则被设置为全局对象。在定义函数的时候绑定,而不是在执行函数的时候绑定。
| 1 | var name = 'window'; | 
其实就是相当于箭头函数外的this是缓存的该箭头函数上层的普通函数的this。如果没有普通函数,则是全局对象(浏览器中则是window)。 也就是说无法通过call、apply、bind绑定箭头函数的this(它自身没有this)。而call、apply、bind可以绑定缓存箭头函数上层的普通函数的this。
| 1 | var student = { | 
call、apply、bind 的 this 指向(显示绑定)
在另一篇文章中
DOM事件处理函数的this指向
addEventerListener、attachEvent、onclick。this 指向触发事件的元素,也就是始事件处理程序所绑定到的DOM节点。
| 1 | var ele = document.getElementById("id"); | 
HTML标签内联事件处理函数的this指向
this 指向所在的DOM元素。第一个是button本身,所以是true,第二个是window。这里跟严格模式没有关系。 
| 1 | <button class="btn1" onclick="console.log(this === document.querySelector('.btn1'))">点我呀</button> | 
jQuery的this指向
在许多情况下JQuery的 this 都指向DOM元素节点
| 1 | $(".btn").on("click",function(){ | 
优先级
new 调用 > call、apply、bind 调用 > 对象上的函数调用 > 普通函数调用
| 1 | var name = 'window'; | 
new 和 apply、call 无法一起使用,这是因为函数内部有两个不同的方法:[[Call]]和[[Constructor]]。 当使用普通函数调用时,[[Call]]会被执行。当使用构造函数调用时,[[Constructor]]会被执行。call、apply、bind和箭头函数内部没有[[Constructor]]方法。
下面比较 new 和  call、apply、bind 调用。在《你不知道的JavaScript》是对比bind和new,引用了mdn的bind的ployfill实现,new调用时bind之后的函数,会忽略bind绑定的第一个参数。
| 1 | function foo(something) { | 
bar 被绑定到 obj1 上,但是 new bar(3) 并没有像我们预计的那样把 obj1.a 修改为3。但是,new 修改了绑定调用 bar() 中的 this。
总结
如果要判断一个函数的this绑定,就需要找到这个函数的直接调用位置。然后可以顺序按照下面四条规则来判断this的绑定对象:
- 普通函数调用: 在严格模式下绑定到 undefined,否则绑定到全局对象。
- 对象上的函数调用:绑定到调用的那个对象
- 由new调用:绑定到新创建的对象(实例),注意:显示return函数或对象,返回值不是新创建的对象,而是显式返回的函数或对象。
- 由call或apply、bind调用:绑定到指定的对象。(严格模式下,绑定到指定的第一个参数。非严格模式下,null和undefined,指向全局对象(浏览器中是window),其余值指向被new Object()包装的对象。)
- 箭头函数不使用上面的绑定规则,根据外层作用域来决定this,继承外层非箭头函数调用的this绑定。
- DOM事件处理函数:一般指向绑定事件的- DOM元素,但有些情况绑定到全局对象(比如- IE6~IE8的- attachEvent)
- HTML标签内联事件处理函数:指向所在的dom元素
- JQuery:在许多情况下都指向DOM元素节点
参考文章
JavaScript深入之从ECMAScript规范解读this
面试官问:JS的this指向https://juejin.cn/post/6844903746984476686#heading-5)